/*
 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package javax.management.relation;


import java.util.ArrayList;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.List;

import java.util.concurrent.atomic.AtomicBoolean;

import static com.sun.jmx.defaults.JmxProperties.RELATION_LOGGER;
import static com.sun.jmx.mbeanserver.Util.cast;

import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.ReflectionException;

/**
 * A RelationSupport object is used internally by the Relation Service to
 * represent simple relations (only roles, no properties or methods), with an
 * unlimited number of roles, of any relation type. As internal representation,
 * it is not exposed to the user.
 * <P>RelationSupport class conforms to the design patterns of standard MBean. So
 * the user can decide to instantiate a RelationSupport object himself as
 * a MBean (as it follows the MBean design patterns), to register it in the
 * MBean Server, and then to add it in the Relation Service.
 * <P>The user can also, when creating his own MBean relation class, have it
 * extending RelationSupport, to retrieve the implementations of required
 * interfaces (see below).
 * <P>It is also possible to have in a user relation MBean class a member
 * being a RelationSupport object, and to implement the required interfaces by
 * delegating all to this member.
 * <P> RelationSupport implements the Relation interface (to be handled by the
 * Relation Service).
 * <P>It implements also the MBeanRegistration interface to be able to retrieve
 * the MBean Server where it is registered (if registered as a MBean) to access
 * to its Relation Service.
 *
 * @since 1.5
 */
public class RelationSupport
    implements RelationSupportMBean, MBeanRegistration {

  //
  // Private members
  //

  // Relation identifier (expected to be unique in the Relation Service where
  // the RelationSupport object will be added)
  private String myRelId = null;

  // ObjectName of the Relation Service where the relation will be added
  // REQUIRED if the RelationSupport is created by the user to be registered as
  // a MBean, as it will have to access the Relation Service via the MBean
  // Server to perform the check regarding the relation type.
  // Is null if current object is directly created by the Relation Service,
  // as the object will directly access it.
  private ObjectName myRelServiceName = null;

  // Reference to the MBean Server where the Relation Service is
  // registered
  // REQUIRED if the RelationSupport is created by the user to be registered as
  // a MBean, as it will have to access the Relation Service via the MBean
  // Server to perform the check regarding the relation type.
  // If the Relationbase object is created by the Relation Service (use of
  // createRelation() method), this is null as not needed, direct access to
  // the Relation Service.
  // If the Relationbase object is created by the user and registered as a
  // MBean, this is set by the preRegister() method below.
  private MBeanServer myRelServiceMBeanServer = null;

  // Relation type name (must be known in the Relation Service where the
  // relation will be added)
  private String myRelTypeName = null;

  // Role map, mapping <role-name> -> <Role>
  // Initialized by role list in the constructor, then updated:
  // - if the relation is a MBean, via setRole() and setRoles() methods, or
  //   via Relation Service setRole() and setRoles() methods
  // - if the relation is internal to the Relation Service, via
  //   setRoleInt() and setRolesInt() methods.
  private final Map<String, Role> myRoleName2ValueMap = new HashMap<String, Role>();

  // Flag to indicate if the object has been added in the Relation Service
  private final AtomicBoolean myInRelServFlg = new AtomicBoolean();

  //
  // Constructors
  //

  /**
   * Creates a {@code RelationSupport} object.
   * <P>This constructor has to be used when the RelationSupport object will
   * be registered as a MBean by the user, or when creating a user relation
   * MBean whose class extends RelationSupport.
   * <P>Nothing is done at the Relation Service level, i.e.
   * the {@code RelationSupport} object is not added to the
   * {@code RelationService} and no checks are performed to
   * see if the provided values are correct.
   * The object is always created, EXCEPT if:
   * <P>- any of the required parameters is {@code null}.
   * <P>- the same name is used for two roles.
   * <P>To be handled as a relation, the {@code RelationSupport} object has
   * to be added to the Relation Service using the Relation Service method
   * addRelation().
   *
   * @param relationId relation identifier, to identify the relation in the Relation Service.
   * <P>Expected to be unique in the given Relation Service.
   * @param relationServiceName ObjectName of the Relation Service where the relation will be
   * registered. <P>This parameter is required as it is the Relation Service that is aware of the
   * definition of the relation type of the given relation, so that will be able to check update
   * operations (set).
   * @param relationTypeName Name of relation type. <P>Expected to have been created in the given
   * Relation Service.
   * @param list list of roles (Role objects) to initialize the relation. Can be {@code null}.
   * <P>Expected to conform to relation info in associated relation type.
   * @throws InvalidRoleValueException if the same name is used for two roles.
   * @throws IllegalArgumentException if any of the required parameters (relation id, relation
   * service ObjectName, or relation type name) is {@code null}.
   */
  public RelationSupport(String relationId,
      ObjectName relationServiceName,
      String relationTypeName,
      RoleList list)
      throws InvalidRoleValueException,
      IllegalArgumentException {

    super();

    RELATION_LOGGER.entering(RelationSupport.class.getName(),
        "RelationSupport");

    // Can throw InvalidRoleValueException and IllegalArgumentException
    initMembers(relationId,
        relationServiceName,
        null,
        relationTypeName,
        list);

    RELATION_LOGGER.exiting(RelationSupport.class.getName(),
        "RelationSupport");
  }

  /**
   * Creates a {@code RelationSupport} object.
   * <P>This constructor has to be used when the user relation MBean
   * implements the interfaces expected to be supported by a relation by
   * delegating to a RelationSupport object.
   * <P>This object needs to know the Relation Service expected to handle the
   * relation. So it has to know the MBean Server where the Relation Service
   * is registered.
   * <P>According to a limitation, a relation MBean must be registered in the
   * same MBean Server as the Relation Service expected to handle it. So the
   * user relation MBean has to be created and registered, and then the
   * wrapped RelationSupport object can be created within the identified MBean
   * Server.
   * <P>Nothing is done at the Relation Service level, i.e.
   * the {@code RelationSupport} object is not added to the
   * {@code RelationService} and no checks are performed to
   * see if the provided values are correct.
   * The object is always created, EXCEPT if:
   * <P>- any of the required parameters is {@code null}.
   * <P>- the same name is used for two roles.
   * <P>To be handled as a relation, the {@code RelationSupport} object has
   * to be added to the Relation Service using the Relation Service method
   * addRelation().
   *
   * @param relationId relation identifier, to identify the relation in the Relation Service.
   * <P>Expected to be unique in the given Relation Service.
   * @param relationServiceName ObjectName of the Relation Service where the relation will be
   * registered. <P>This parameter is required as it is the Relation Service that is aware of the
   * definition of the relation type of the given relation, so that will be able to check update
   * operations (set).
   * @param relationServiceMBeanServer MBean Server where the wrapping MBean is or will be
   * registered. <P>Expected to be the MBean Server where the Relation Service is or will be
   * registered.
   * @param relationTypeName Name of relation type. <P>Expected to have been created in the given
   * Relation Service.
   * @param list list of roles (Role objects) to initialize the relation. Can be {@code null}.
   * <P>Expected to conform to relation info in associated relation type.
   * @throws InvalidRoleValueException if the same name is used for two roles.
   * @throws IllegalArgumentException if any of the required parameters (relation id, relation
   * service ObjectName, relation service MBeanServer, or relation type name) is {@code null}.
   */
  public RelationSupport(String relationId,
      ObjectName relationServiceName,
      MBeanServer relationServiceMBeanServer,
      String relationTypeName,
      RoleList list)
      throws InvalidRoleValueException,
      IllegalArgumentException {

    super();

    if (relationServiceMBeanServer == null) {
      String excMsg = "Invalid parameter.";
      throw new IllegalArgumentException(excMsg);
    }

    RELATION_LOGGER.entering(RelationSupport.class.getName(),
        "RelationSupport");

    // Can throw InvalidRoleValueException and
    // IllegalArgumentException
    initMembers(relationId,
        relationServiceName,
        relationServiceMBeanServer,
        relationTypeName,
        list);

    RELATION_LOGGER.exiting(RelationSupport.class.getName(),
        "RelationSupport");
  }

  //
  // Relation Interface
  //

  /**
   * Retrieves role value for given role name.
   * <P>Checks if the role exists and is readable according to the relation
   * type.
   *
   * @param roleName name of role
   * @return the ArrayList of ObjectName objects being the role value
   * @throws IllegalArgumentException if null role name
   * @throws RoleNotFoundException if: <P>- there is no role with given name <P>- the role is not
   * readable.
   * @throws RelationServiceNotRegisteredException if the Relation Service is not registered in the
   * MBean Server
   * @see #setRole
   */
  public List<ObjectName> getRole(String roleName)
      throws IllegalArgumentException,
      RoleNotFoundException,
      RelationServiceNotRegisteredException {

    if (roleName == null) {
      String excMsg = "Invalid parameter.";
      throw new IllegalArgumentException(excMsg);
    }

    RELATION_LOGGER.entering(RelationSupport.class.getName(),
        "getRole", roleName);

    // Can throw RoleNotFoundException and
    // RelationServiceNotRegisteredException
    List<ObjectName> result = cast(
        getRoleInt(roleName, false, null, false));

    RELATION_LOGGER.exiting(RelationSupport.class.getName(), "getRole");
    return result;
  }

  /**
   * Retrieves values of roles with given names.
   * <P>Checks for each role if it exists and is readable according to the
   * relation type.
   *
   * @param roleNameArray array of names of roles to be retrieved
   * @return a RoleResult object, including a RoleList (for roles successfully retrieved) and a
   * RoleUnresolvedList (for roles not retrieved).
   * @throws IllegalArgumentException if null role name
   * @throws RelationServiceNotRegisteredException if the Relation Service is not registered in the
   * MBean Server
   * @see #setRoles
   */
  public RoleResult getRoles(String[] roleNameArray)
      throws IllegalArgumentException,
      RelationServiceNotRegisteredException {

    if (roleNameArray == null) {
      String excMsg = "Invalid parameter.";
      throw new IllegalArgumentException(excMsg);
    }

    RELATION_LOGGER.entering(RelationSupport.class.getName(), "getRoles");

    // Can throw RelationServiceNotRegisteredException
    RoleResult result = getRolesInt(roleNameArray, false, null);

    RELATION_LOGGER.exiting(RelationSupport.class.getName(), "getRoles");
    return result;
  }

  /**
   * Returns all roles present in the relation.
   *
   * @return a RoleResult object, including a RoleList (for roles successfully retrieved) and a
   * RoleUnresolvedList (for roles not readable).
   * @throws RelationServiceNotRegisteredException if the Relation Service is not registered in the
   * MBean Server
   */
  public RoleResult getAllRoles()
      throws RelationServiceNotRegisteredException {

    RELATION_LOGGER.entering(RelationSupport.class.getName(),
        "getAllRoles");

    RoleResult result = null;
    try {
      result = getAllRolesInt(false, null);
    } catch (IllegalArgumentException exc) {
      // OK : Invalid parameters, ignore...
    }

    RELATION_LOGGER.exiting(RelationSupport.class.getName(), "getAllRoles");
    return result;
  }

  /**
   * Returns all roles in the relation without checking read mode.
   *
   * @return a RoleList
   */
  public RoleList retrieveAllRoles() {

    RELATION_LOGGER.entering(RelationSupport.class.getName(),
        "retrieveAllRoles");

    RoleList result;
    synchronized (myRoleName2ValueMap) {
      result =
          new RoleList(new ArrayList<Role>(myRoleName2ValueMap.values()));
    }

    RELATION_LOGGER.exiting(RelationSupport.class.getName(),
        "retrieveAllRoles");
    return result;
  }

  /**
   * Returns the number of MBeans currently referenced in the given role.
   *
   * @param roleName name of role
   * @return the number of currently referenced MBeans in that role
   * @throws IllegalArgumentException if null role name
   * @throws RoleNotFoundException if there is no role with given name
   */
  public Integer getRoleCardinality(String roleName)
      throws IllegalArgumentException,
      RoleNotFoundException {

    if (roleName == null) {
      String excMsg = "Invalid parameter.";
      throw new IllegalArgumentException(excMsg);
    }

    RELATION_LOGGER.entering(RelationSupport.class.getName(),
        "getRoleCardinality", roleName);

    // Try to retrieve the role
    Role role;
    synchronized (myRoleName2ValueMap) {
      // No null Role is allowed, so direct use of get()
      role = (myRoleName2ValueMap.get(roleName));
    }
    if (role == null) {
      int pbType = RoleStatus.NO_ROLE_WITH_NAME;
      // Will throw a RoleNotFoundException
      //
      // Will not throw InvalidRoleValueException, so catch it for the
      // compiler
      try {
        RelationService.throwRoleProblemException(pbType,
            roleName);
      } catch (InvalidRoleValueException exc) {
        // OK : Do not throw InvalidRoleValueException as
        //      a RoleNotFoundException will be thrown.
      }
    }

    List<ObjectName> roleValue = role.getRoleValue();

    RELATION_LOGGER.exiting(RelationSupport.class.getName(),
        "getRoleCardinality");
    return roleValue.size();
  }

  /**
   * Sets the given role.
   * <P>Will check the role according to its corresponding role definition
   * provided in relation's relation type
   * <P>Will send a notification (RelationNotification with type
   * RELATION_BASIC_UPDATE or RELATION_MBEAN_UPDATE, depending if the
   * relation is a MBean or not).
   *
   * @param role role to be set (name and new value)
   * @throws IllegalArgumentException if null role
   * @throws RoleNotFoundException if there is no role with the supplied role's name or if the role
   * is not writable (no test on the write access mode performed when initializing the role)
   * @throws InvalidRoleValueException if value provided for role is not valid, i.e.: <P>- the
   * number of referenced MBeans in given value is less than expected minimum degree <P>- the number
   * of referenced MBeans in provided value exceeds expected maximum degree <P>- one referenced
   * MBean in the value is not an Object of the MBean class expected for that role <P>- a MBean
   * provided for that role does not exist
   * @throws RelationServiceNotRegisteredException if the Relation Service is not registered in the
   * MBean Server
   * @throws RelationTypeNotFoundException if the relation type has not been declared in the
   * Relation Service
   * @throws RelationNotFoundException if the relation has not been added in the Relation Service.
   * @see #getRole
   */
  public void setRole(Role role)
      throws IllegalArgumentException,
      RoleNotFoundException,
      RelationTypeNotFoundException,
      InvalidRoleValueException,
      RelationServiceNotRegisteredException,
      RelationNotFoundException {

    if (role == null) {
      String excMsg = "Invalid parameter.";
      throw new IllegalArgumentException(excMsg);
    }

    RELATION_LOGGER.entering(RelationSupport.class.getName(),
        "setRole", role);

    // Will return null :)
    Object result = setRoleInt(role, false, null, false);

    RELATION_LOGGER.exiting(RelationSupport.class.getName(), "setRole");
    return;
  }

  /**
   * Sets the given roles.
   * <P>Will check the role according to its corresponding role definition
   * provided in relation's relation type
   * <P>Will send one notification (RelationNotification with type
   * RELATION_BASIC_UPDATE or RELATION_MBEAN_UPDATE, depending if the
   * relation is a MBean or not) per updated role.
   *
   * @param list list of roles to be set
   * @return a RoleResult object, including a RoleList (for roles successfully set) and a
   * RoleUnresolvedList (for roles not set).
   * @throws IllegalArgumentException if null role list
   * @throws RelationServiceNotRegisteredException if the Relation Service is not registered in the
   * MBean Server
   * @throws RelationTypeNotFoundException if the relation type has not been declared in the
   * Relation Service.
   * @throws RelationNotFoundException if the relation MBean has not been added in the Relation
   * Service.
   * @see #getRoles
   */
  public RoleResult setRoles(RoleList list)
      throws IllegalArgumentException,
      RelationServiceNotRegisteredException,
      RelationTypeNotFoundException,
      RelationNotFoundException {

    if (list == null) {
      String excMsg = "Invalid parameter.";
      throw new IllegalArgumentException(excMsg);
    }

    RELATION_LOGGER.entering(RelationSupport.class.getName(),
        "setRoles", list);

    RoleResult result = setRolesInt(list, false, null);

    RELATION_LOGGER.exiting(RelationSupport.class.getName(), "setRoles");
    return result;
  }

  /**
   * Callback used by the Relation Service when a MBean referenced in a role
   * is unregistered.
   * <P>The Relation Service will call this method to let the relation
   * take action to reflect the impact of such unregistration.
   * <P>BEWARE. the user is not expected to call this method.
   * <P>Current implementation is to set the role with its current value
   * (list of ObjectNames of referenced MBeans) without the unregistered
   * one.
   *
   * @param objectName ObjectName of unregistered MBean
   * @param roleName name of role where the MBean is referenced
   * @throws IllegalArgumentException if null parameter
   * @throws RoleNotFoundException if role does not exist in the relation or is not writable
   * @throws InvalidRoleValueException if role value does not conform to the associated role info
   * (this will never happen when called from the Relation Service)
   * @throws RelationServiceNotRegisteredException if the Relation Service is not registered in the
   * MBean Server
   * @throws RelationTypeNotFoundException if the relation type has not been declared in the
   * Relation Service.
   * @throws RelationNotFoundException if this method is called for a relation MBean not added in
   * the Relation Service.
   */
  public void handleMBeanUnregistration(ObjectName objectName,
      String roleName)
      throws IllegalArgumentException,
      RoleNotFoundException,
      InvalidRoleValueException,
      RelationServiceNotRegisteredException,
      RelationTypeNotFoundException,
      RelationNotFoundException {

    if (objectName == null || roleName == null) {
      String excMsg = "Invalid parameter.";
      throw new IllegalArgumentException(excMsg);
    }

    RELATION_LOGGER.entering(RelationSupport.class.getName(),
        "handleMBeanUnregistration",
        new Object[]{objectName, roleName});

    // Can throw RoleNotFoundException, InvalidRoleValueException,
    // or RelationTypeNotFoundException
    handleMBeanUnregistrationInt(objectName,
        roleName,
        false,
        null);

    RELATION_LOGGER.exiting(RelationSupport.class.getName(),
        "handleMBeanUnregistration");
    return;
  }

  /**
   * Retrieves MBeans referenced in the various roles of the relation.
   *
   * @return a HashMap mapping: <P> ObjectName {@literal ->} ArrayList of String (role names)
   */
  public Map<ObjectName, List<String>> getReferencedMBeans() {

    RELATION_LOGGER.entering(RelationSupport.class.getName(),
        "getReferencedMBeans");

    Map<ObjectName, List<String>> refMBeanMap =
        new HashMap<ObjectName, List<String>>();

    synchronized (myRoleName2ValueMap) {

      for (Role currRole : myRoleName2ValueMap.values()) {

        String currRoleName = currRole.getRoleName();
        // Retrieves ObjectNames of MBeans referenced in current role
        List<ObjectName> currRefMBeanList = currRole.getRoleValue();

        for (ObjectName currRoleObjName : currRefMBeanList) {

          // Sees if current MBean has been already referenced in
          // roles already seen
          List<String> mbeanRoleNameList =
              refMBeanMap.get(currRoleObjName);

          boolean newRefFlg = false;
          if (mbeanRoleNameList == null) {
            newRefFlg = true;
            mbeanRoleNameList = new ArrayList<String>();
          }
          mbeanRoleNameList.add(currRoleName);
          if (newRefFlg) {
            refMBeanMap.put(currRoleObjName, mbeanRoleNameList);
          }
        }
      }
    }

    RELATION_LOGGER.exiting(RelationSupport.class.getName(),
        "getReferencedMBeans");
    return refMBeanMap;
  }

  /**
   * Returns name of associated relation type.
   */
  public String getRelationTypeName() {
    return myRelTypeName;
  }

  /**
   * Returns ObjectName of the Relation Service handling the relation.
   *
   * @return the ObjectName of the Relation Service.
   */
  public ObjectName getRelationServiceName() {
    return myRelServiceName;
  }

  /**
   * Returns relation identifier (used to uniquely identify the relation
   * inside the Relation Service).
   *
   * @return the relation id.
   */
  public String getRelationId() {
    return myRelId;
  }

  //
  // MBeanRegistration interface
  //

  // Pre-registration: retrieves the MBean Server (useful to access to the
  // Relation Service)
  // This is the way to retrieve the MBean Server when the relation object is
  // a MBean created by the user outside of the Relation Service.
  //
  // No exception thrown.
  public ObjectName preRegister(MBeanServer server,
      ObjectName name)
      throws Exception {

    myRelServiceMBeanServer = server;
    return name;
  }

  // Post-registration: does nothing
  public void postRegister(Boolean registrationDone) {
    return;
  }

  // Pre-unregistration: does nothing
  public void preDeregister()
      throws Exception {
    return;
  }

  // Post-unregistration: does nothing
  public void postDeregister() {
    return;
  }

  //
  // Others
  //

  /**
   * Returns an internal flag specifying if the object is still handled by
   * the Relation Service.
   */
  public Boolean isInRelationService() {
    return myInRelServFlg.get();
  }

  public void setRelationServiceManagementFlag(Boolean flag)
      throws IllegalArgumentException {

    if (flag == null) {
      String excMsg = "Invalid parameter.";
      throw new IllegalArgumentException(excMsg);
    }
    myInRelServFlg.set(flag);
  }

  //
  // Misc
  //

  // Gets the role with given name
  // Checks if the role exists and is readable according to the relation
  // type.
  //
  // This method is called in getRole() above.
  // It is also called in the Relation Service getRole() method.
  // It is also called in getRolesInt() below (used for getRoles() above
  // and for Relation Service getRoles() method).
  //
  // Depending on parameters reflecting its use (either in the scope of
  // getting a single role or of getting several roles), will return:
  // - in case of success:
  //   - for single role retrieval, the ArrayList of ObjectNames being the
  //     role value
  //   - for multi-role retrieval, the Role object itself
  // - in case of failure (except critical exceptions):
  //   - for single role retrieval, if role does not exist or is not
  //     readable, an RoleNotFoundException exception is raised
  //   - for multi-role retrieval, a RoleUnresolved object
  //
  // -param roleName  name of role to be retrieved
  // -param relationServCallFlg  true if call from the Relation Service; this
  //  will happen if the current RelationSupport object has been created by
  //  the Relation Service (via createRelation()) method, so direct access is
  //  possible.
  // -param relationServ  reference to Relation Service object, if object
  //  created by Relation Service.
  // -param multiRoleFlg  true if getting the role in the scope of a
  //  multiple retrieval.
  //
  // -return:
  //  - for single role retrieval (multiRoleFlg false):
  //    - ArrayList of ObjectName objects, value of role with given name, if
  //      the role can be retrieved
  //    - raise a RoleNotFoundException exception else
  //  - for multi-role retrieval (multiRoleFlg true):
  //    - the Role object for given role name if role can be retrieved
  //    - a RoleUnresolved object with problem.
  //
  // -exception IllegalArgumentException  if null parameter
  // -exception RoleNotFoundException  if multiRoleFlg is false and:
  //  - there is no role with given name
  //  or
  //  - the role is not readable.
  // -exception RelationServiceNotRegisteredException  if the Relation
  //  Service is not registered in the MBean Server
  Object getRoleInt(String roleName,
      boolean relationServCallFlg,
      RelationService relationServ,
      boolean multiRoleFlg)
      throws IllegalArgumentException,
      RoleNotFoundException,
      RelationServiceNotRegisteredException {

    if (roleName == null ||
        (relationServCallFlg && relationServ == null)) {
      String excMsg = "Invalid parameter.";
      throw new IllegalArgumentException(excMsg);
    }

    RELATION_LOGGER.entering(RelationSupport.class.getName(),
        "getRoleInt", roleName);

    int pbType = 0;

    Role role;
    synchronized (myRoleName2ValueMap) {
      // No null Role is allowed, so direct use of get()
      role = (myRoleName2ValueMap.get(roleName));
    }

    if (role == null) {
      pbType = RoleStatus.NO_ROLE_WITH_NAME;

    } else {
      // Checks if the role is readable
      Integer status;

      if (relationServCallFlg) {

        // Call from the Relation Service, so direct access to it,
        // avoiding MBean Server
        // Shall not throw a RelationTypeNotFoundException
        try {
          status = relationServ.checkRoleReading(roleName,
              myRelTypeName);
        } catch (RelationTypeNotFoundException exc) {
          throw new RuntimeException(exc.getMessage());
        }

      } else {

        // Call from getRole() method above
        // So we have a MBean. We must access the Relation Service
        // via the MBean Server.
        Object[] params = new Object[2];
        params[0] = roleName;
        params[1] = myRelTypeName;
        String[] signature = new String[2];
        signature[0] = "java.lang.String";
        signature[1] = "java.lang.String";
        // Can throw InstanceNotFoundException if the Relation
        // Service is not registered (to be catched in any case and
        // transformed into RelationServiceNotRegisteredException).
        //
        // Shall not throw a MBeanException, or a ReflectionException
        // or an InstanceNotFoundException
        try {
          status = (Integer)
              (myRelServiceMBeanServer.invoke(myRelServiceName,
                  "checkRoleReading",
                  params,
                  signature));
        } catch (MBeanException exc1) {
          throw new RuntimeException("incorrect relation type");
        } catch (ReflectionException exc2) {
          throw new RuntimeException(exc2.getMessage());
        } catch (InstanceNotFoundException exc3) {
          throw new RelationServiceNotRegisteredException(
              exc3.getMessage());
        }
      }

      pbType = status.intValue();
    }

    Object result;

    if (pbType == 0) {
      // Role can be retrieved

      if (!(multiRoleFlg)) {
        // Single role retrieved: returns its value
        // Note: no need to test if role value (list) not null before
        //       cloning, null value not allowed, empty list if
        //       nothing.
        result = new ArrayList<ObjectName>(role.getRoleValue());

      } else {
        // Role retrieved during multi-role retrieval: returns the
        // role
        result = (Role) (role.clone());
      }

    } else {
      // Role not retrieved

      if (!(multiRoleFlg)) {
        // Problem when retrieving a simple role: either role not
        // found or not readable, so raises a RoleNotFoundException.
        try {
          RelationService.throwRoleProblemException(pbType,
              roleName);
          // To keep compiler happy :)
          return null;
        } catch (InvalidRoleValueException exc) {
          throw new RuntimeException(exc.getMessage());
        }

      } else {
        // Problem when retrieving a role in a multi-role retrieval:
        // returns a RoleUnresolved object
        result = new RoleUnresolved(roleName, null, pbType);
      }
    }

    RELATION_LOGGER.exiting(RelationSupport.class.getName(), "getRoleInt");
    return result;
  }

  // Gets the given roles
  // For each role, verifies if the role exists and is readable according to
  // the relation type.
  //
  // This method is called in getRoles() above and in Relation Service
  // getRoles() method.
  //
  // -param roleNameArray  array of names of roles to be retrieved
  // -param relationServCallFlg  true if call from the Relation Service; this
  //  will happen if the current RelationSupport object has been created by
  //  the Relation Service (via createRelation()) method, so direct access is
  //  possible.
  // -param relationServ  reference to Relation Service object, if object
  //  created by Relation Service.
  //
  // -return a RoleResult object
  //
  // -exception IllegalArgumentException  if null parameter
  // -exception RelationServiceNotRegisteredException  if the Relation
  //  Service is not registered in the MBean Server
  RoleResult getRolesInt(String[] roleNameArray,
      boolean relationServCallFlg,
      RelationService relationServ)
      throws IllegalArgumentException,
      RelationServiceNotRegisteredException {

    if (roleNameArray == null ||
        (relationServCallFlg && relationServ == null)) {
      String excMsg = "Invalid parameter.";
      throw new IllegalArgumentException(excMsg);
    }

    RELATION_LOGGER.entering(RelationSupport.class.getName(),
        "getRolesInt");

    RoleList roleList = new RoleList();
    RoleUnresolvedList roleUnresList = new RoleUnresolvedList();

    for (int i = 0; i < roleNameArray.length; i++) {
      String currRoleName = roleNameArray[i];

      Object currResult;

      // Can throw RelationServiceNotRegisteredException
      //
      // RoleNotFoundException: not possible but catch it for compiler :)
      try {
        currResult = getRoleInt(currRoleName,
            relationServCallFlg,
            relationServ,
            true);

      } catch (RoleNotFoundException exc) {
        return null; // :)
      }

      if (currResult instanceof Role) {
        // Can throw IllegalArgumentException if role is null
        // (normally should not happen :(
        try {
          roleList.add((Role) currResult);
        } catch (IllegalArgumentException exc) {
          throw new RuntimeException(exc.getMessage());
        }

      } else if (currResult instanceof RoleUnresolved) {
        // Can throw IllegalArgumentException if role is null
        // (normally should not happen :(
        try {
          roleUnresList.add((RoleUnresolved) currResult);
        } catch (IllegalArgumentException exc) {
          throw new RuntimeException(exc.getMessage());
        }
      }
    }

    RoleResult result = new RoleResult(roleList, roleUnresList);
    RELATION_LOGGER.exiting(RelationSupport.class.getName(),
        "getRolesInt");
    return result;
  }

  // Returns all roles present in the relation
  //
  // -return a RoleResult object, including a RoleList (for roles
  //  successfully retrieved) and a RoleUnresolvedList (for roles not
  //  readable).
  //
  // -exception IllegalArgumentException if null parameter
  // -exception RelationServiceNotRegisteredException  if the Relation
  //  Service is not registered in the MBean Server
  //
  RoleResult getAllRolesInt(boolean relationServCallFlg,
      RelationService relationServ)
      throws IllegalArgumentException,
      RelationServiceNotRegisteredException {

    if (relationServCallFlg && relationServ == null) {
      String excMsg = "Invalid parameter.";
      throw new IllegalArgumentException(excMsg);
    }

    RELATION_LOGGER.entering(RelationSupport.class.getName(),
        "getAllRolesInt");

    List<String> roleNameList;
    synchronized (myRoleName2ValueMap) {
      roleNameList =
          new ArrayList<String>(myRoleName2ValueMap.keySet());
    }
    String[] roleNames = new String[roleNameList.size()];
    roleNameList.toArray(roleNames);

    RoleResult result = getRolesInt(roleNames,
        relationServCallFlg,
        relationServ);

    RELATION_LOGGER.exiting(RelationSupport.class.getName(),
        "getAllRolesInt");
    return result;
  }

  // Sets the role with given value
  //
  // This method is called in setRole() above.
  // It is also called by the Relation Service setRole() method.
  // It is also called in setRolesInt() method below (used in setRoles()
  // above and in RelationService setRoles() method).
  //
  // Will check the role according to its corresponding role definition
  // provided in relation's relation type
  // Will send a notification (RelationNotification with type
  // RELATION_BASIC_UPDATE or RELATION_MBEAN_UPDATE, depending if the
  // relation is a MBean or not) if not initialization of role.
  //
  // -param aRole  role to be set (name and new value)
  // -param relationServCallFlg  true if call from the Relation Service; this
  //  will happen if the current RelationSupport object has been created by
  //  the Relation Service (via createRelation()) method, so direct access is
  //  possible.
  // -param relationServ  reference to Relation Service object, if internal
  //  relation
  // -param multiRoleFlg  true if getting the role in the scope of a
  //  multiple retrieval.
  //
  // -return (except other "critical" exceptions):
  //  - for single role retrieval (multiRoleFlg false):
  //    - null if the role has been set
  //    - raise an InvalidRoleValueException
  // else
  //  - for multi-role retrieval (multiRoleFlg true):
  //    - the Role object for given role name if role has been set
  //    - a RoleUnresolved object with problem else.
  //
  // -exception IllegalArgumentException if null parameter
  // -exception RoleNotFoundException  if multiRoleFlg is false and:
  //  - internal relation and the role does not exist
  //  or
  //  - existing role (i.e. not initializing it) and the role is not
  //    writable.
  // -exception InvalidRoleValueException  ifmultiRoleFlg is false and
  //  value provided for:
  //   - the number of referenced MBeans in given value is less than
  //     expected minimum degree
  //   or
  //   - the number of referenced MBeans in provided value exceeds expected
  //     maximum degree
  //   or
  //   - one referenced MBean in the value is not an Object of the MBean
  //     class expected for that role
  //   or
  //   - a MBean provided for that role does not exist
  // -exception RelationServiceNotRegisteredException  if the Relation
  //  Service is not registered in the MBean Server
  // -exception RelationTypeNotFoundException  if relation type unknown
  // -exception RelationNotFoundException  if a relation MBean has not been
  //  added in the Relation Service
  Object setRoleInt(Role aRole,
      boolean relationServCallFlg,
      RelationService relationServ,
      boolean multiRoleFlg)
      throws IllegalArgumentException,
      RoleNotFoundException,
      InvalidRoleValueException,
      RelationServiceNotRegisteredException,
      RelationTypeNotFoundException,
      RelationNotFoundException {

    if (aRole == null ||
        (relationServCallFlg && relationServ == null)) {
      String excMsg = "Invalid parameter.";
      throw new IllegalArgumentException(excMsg);
    }

    RELATION_LOGGER.entering(RelationSupport.class.getName(),
        "setRoleInt", new Object[]{aRole, relationServCallFlg,
            relationServ, multiRoleFlg});

    String roleName = aRole.getRoleName();
    int pbType = 0;

    // Checks if role exists in the relation
    // No error if the role does not exist in the relation, to be able to
    // handle initialization of role when creating the relation
    // (roles provided in the RoleList parameter are directly set but
    // roles automatically initialized are set using setRole())
    Role role;
    synchronized (myRoleName2ValueMap) {
      role = (myRoleName2ValueMap.get(roleName));
    }

    List<ObjectName> oldRoleValue;
    Boolean initFlg;

    if (role == null) {
      initFlg = true;
      oldRoleValue = new ArrayList<ObjectName>();

    } else {
      initFlg = false;
      oldRoleValue = role.getRoleValue();
    }

    // Checks if the role can be set: is writable (except if
    // initialization) and correct value
    try {
      Integer status;

      if (relationServCallFlg) {

        // Call from the Relation Service, so direct access to it,
        // avoiding MBean Server
        //
        // Shall not raise a RelationTypeNotFoundException
        status = relationServ.checkRoleWriting(aRole,
            myRelTypeName,
            initFlg);

      } else {

        // Call from setRole() method above
        // So we have a MBean. We must access the Relation Service
        // via the MBean Server.
        Object[] params = new Object[3];
        params[0] = aRole;
        params[1] = myRelTypeName;
        params[2] = initFlg;
        String[] signature = new String[3];
        signature[0] = "javax.management.relation.Role";
        signature[1] = "java.lang.String";
        signature[2] = "java.lang.Boolean";
        // Can throw InstanceNotFoundException if the Relation Service
        // is not registered (to be transformed into
        // RelationServiceNotRegisteredException in any case).
        //
        // Can throw a MBeanException wrapping a
        // RelationTypeNotFoundException:
        // throw wrapped exception.
        //
        // Shall not throw a ReflectionException
        status = (Integer)
            (myRelServiceMBeanServer.invoke(myRelServiceName,
                "checkRoleWriting",
                params,
                signature));
      }

      pbType = status.intValue();

    } catch (MBeanException exc2) {

      // Retrieves underlying exception
      Exception wrappedExc = exc2.getTargetException();
      if (wrappedExc instanceof RelationTypeNotFoundException) {
        throw ((RelationTypeNotFoundException) wrappedExc);

      } else {
        throw new RuntimeException(wrappedExc.getMessage());
      }

    } catch (ReflectionException exc3) {
      throw new RuntimeException(exc3.getMessage());

    } catch (RelationTypeNotFoundException exc4) {
      throw new RuntimeException(exc4.getMessage());

    } catch (InstanceNotFoundException exc5) {
      throw new RelationServiceNotRegisteredException(exc5.getMessage());
    }

    Object result = null;

    if (pbType == 0) {
      // Role can be set
      if (!(initFlg.booleanValue())) {

        // Not initializing the role
        // If role being initialized:
        // - do not send an update notification
        // - do not try to update internal map of Relation Service
        //   listing referenced MBeans, as role is initialized to an
        //   empty list

        // Sends a notification (RelationNotification)
        // Can throw a RelationNotFoundException
        sendRoleUpdateNotification(aRole,
            oldRoleValue,
            relationServCallFlg,
            relationServ);

        // Updates the role map of the Relation Service
        // Can throw RelationNotFoundException
        updateRelationServiceMap(aRole,
            oldRoleValue,
            relationServCallFlg,
            relationServ);

      }

      // Sets the role
      synchronized (myRoleName2ValueMap) {
        myRoleName2ValueMap.put(roleName,
            (Role) (aRole.clone()));
      }

      // Single role set: returns null: nothing to set in result

      if (multiRoleFlg) {
        // Multi-roles retrieval: returns the role
        result = aRole;
      }

    } else {

      // Role not set

      if (!(multiRoleFlg)) {
        // Problem when setting a simple role: either role not
        // found, not writable, or incorrect value:
        // raises appropriate exception, RoleNotFoundException or
        // InvalidRoleValueException
        RelationService.throwRoleProblemException(pbType,
            roleName);
        // To keep compiler happy :)
        return null;

      } else {
        // Problem when retrieving a role in a multi-role retrieval:
        // returns a RoleUnresolved object
        result = new RoleUnresolved(roleName,
            aRole.getRoleValue(),
            pbType);
      }
    }

    RELATION_LOGGER.exiting(RelationSupport.class.getName(), "setRoleInt");
    return result;
  }

  // Requires the Relation Service to send a notification
  // RelationNotification, with type being either:
  // - RelationNotification.RELATION_BASIC_UPDATE if the updated relation is
  //   a relation internal to the Relation Service
  // - RelationNotification.RELATION_MBEAN_UPDATE if the updated relation is
  //   a relation MBean.
  //
  // -param newRole  new role
  // -param oldRoleValue  old role value (ArrayList of ObjectNames)
  // -param relationServCallFlg  true if call from the Relation Service; this
  //  will happen if the current RelationSupport object has been created by
  //  the Relation Service (via createRelation()) method, so direct access is
  //  possible.
  // -param relationServ  reference to Relation Service object, if object
  //  created by Relation Service.
  //
  // -exception IllegalArgumentException  if null parameter provided
  // -exception RelationServiceNotRegisteredException  if the Relation
  //  Service is not registered in the MBean Server
  // -exception RelationNotFoundException if:
  //  - relation MBean
  //  and
  //  - it has not been added into the Relation Service
  private void sendRoleUpdateNotification(Role newRole,
      List<ObjectName> oldRoleValue,
      boolean relationServCallFlg,
      RelationService relationServ)
      throws IllegalArgumentException,
      RelationServiceNotRegisteredException,
      RelationNotFoundException {

    if (newRole == null ||
        oldRoleValue == null ||
        (relationServCallFlg && relationServ == null)) {
      String excMsg = "Invalid parameter.";
      throw new IllegalArgumentException(excMsg);
    }

    RELATION_LOGGER.entering(RelationSupport.class.getName(),
        "sendRoleUpdateNotification", new Object[]{newRole,
            oldRoleValue, relationServCallFlg, relationServ});

    if (relationServCallFlg) {
      // Direct call to the Relation Service
      // Shall not throw a RelationNotFoundException for an internal
      // relation
      try {
        relationServ.sendRoleUpdateNotification(myRelId,
            newRole,
            oldRoleValue);
      } catch (RelationNotFoundException exc) {
        throw new RuntimeException(exc.getMessage());
      }

    } else {

      Object[] params = new Object[3];
      params[0] = myRelId;
      params[1] = newRole;
      params[2] = oldRoleValue;
      String[] signature = new String[3];
      signature[0] = "java.lang.String";
      signature[1] = "javax.management.relation.Role";
      signature[2] = "java.util.List";

      // Can throw InstanceNotFoundException if the Relation Service
      // is not registered (to be transformed).
      //
      // Can throw a MBeanException wrapping a
      // RelationNotFoundException (to be raised in any case): wrapped
      // exception to be thrown
      //
      // Shall not throw a ReflectionException
      try {
        myRelServiceMBeanServer.invoke(myRelServiceName,
            "sendRoleUpdateNotification",
            params,
            signature);
      } catch (ReflectionException exc1) {
        throw new RuntimeException(exc1.getMessage());
      } catch (InstanceNotFoundException exc2) {
        throw new RelationServiceNotRegisteredException(
            exc2.getMessage());
      } catch (MBeanException exc3) {
        Exception wrappedExc = exc3.getTargetException();
        if (wrappedExc instanceof RelationNotFoundException) {
          throw ((RelationNotFoundException) wrappedExc);
        } else {
          throw new RuntimeException(wrappedExc.getMessage());
        }
      }
    }

    RELATION_LOGGER.exiting(RelationSupport.class.getName(),
        "sendRoleUpdateNotification");
    return;
  }

  // Requires the Relation Service to update its internal map handling
  // MBeans referenced in relations.
  // The Relation Service will also update its recording as a listener to
  // be informed about unregistration of new referenced MBeans, and no longer
  // informed of MBeans no longer referenced.
  //
  // -param newRole  new role
  // -param oldRoleValue  old role value (ArrayList of ObjectNames)
  // -param relationServCallFlg  true if call from the Relation Service; this
  //  will happen if the current RelationSupport object has been created by
  //  the Relation Service (via createRelation()) method, so direct access is
  //  possible.
  // -param relationServ  reference to Relation Service object, if object
  //  created by Relation Service.
  //
  // -exception IllegalArgumentException  if null parameter
  // -exception RelationServiceNotRegisteredException  if the Relation
  //  Service is not registered in the MBean Server
  // -exception RelationNotFoundException if:
  //  - relation MBean
  //  and
  //  - the relation is not added in the Relation Service
  private void updateRelationServiceMap(Role newRole,
      List<ObjectName> oldRoleValue,
      boolean relationServCallFlg,
      RelationService relationServ)
      throws IllegalArgumentException,
      RelationServiceNotRegisteredException,
      RelationNotFoundException {

    if (newRole == null ||
        oldRoleValue == null ||
        (relationServCallFlg && relationServ == null)) {
      String excMsg = "Invalid parameter.";
      throw new IllegalArgumentException(excMsg);
    }

    RELATION_LOGGER.entering(RelationSupport.class.getName(),
        "updateRelationServiceMap", new Object[]{newRole,
            oldRoleValue, relationServCallFlg, relationServ});

    if (relationServCallFlg) {
      // Direct call to the Relation Service
      // Shall not throw a RelationNotFoundException
      try {
        relationServ.updateRoleMap(myRelId,
            newRole,
            oldRoleValue);
      } catch (RelationNotFoundException exc) {
        throw new RuntimeException(exc.getMessage());
      }

    } else {
      Object[] params = new Object[3];
      params[0] = myRelId;
      params[1] = newRole;
      params[2] = oldRoleValue;
      String[] signature = new String[3];
      signature[0] = "java.lang.String";
      signature[1] = "javax.management.relation.Role";
      signature[2] = "java.util.List";
      // Can throw InstanceNotFoundException if the Relation Service
      // is not registered (to be transformed).
      // Can throw a MBeanException wrapping a RelationNotFoundException:
      // wrapped exception to be thrown
      //
      // Shall not throw a ReflectionException
      try {
        myRelServiceMBeanServer.invoke(myRelServiceName,
            "updateRoleMap",
            params,
            signature);
      } catch (ReflectionException exc1) {
        throw new RuntimeException(exc1.getMessage());
      } catch (InstanceNotFoundException exc2) {
        throw new
            RelationServiceNotRegisteredException(exc2.getMessage());
      } catch (MBeanException exc3) {
        Exception wrappedExc = exc3.getTargetException();
        if (wrappedExc instanceof RelationNotFoundException) {
          throw ((RelationNotFoundException) wrappedExc);
        } else {
          throw new RuntimeException(wrappedExc.getMessage());
        }
      }
    }

    RELATION_LOGGER.exiting(RelationSupport.class.getName(),
        "updateRelationServiceMap");
    return;
  }

  // Sets the given roles
  // For each role:
  // - will check the role according to its corresponding role definition
  //   provided in relation's relation type
  // - will send a notification (RelationNotification with type
  //   RELATION_BASIC_UPDATE or RELATION_MBEAN_UPDATE, depending if the
  //   relation is a MBean or not) for each updated role.
  //
  // This method is called in setRoles() above and in Relation Service
  // setRoles() method.
  //
  // -param list  list of roles to be set
  // -param relationServCallFlg  true if call from the Relation Service; this
  //  will happen if the current RelationSupport object has been created by
  //  the Relation Service (via createRelation()) method, so direct access is
  //  possible.
  // -param relationServ  reference to Relation Service object, if object
  //  created by Relation Service.
  //
  // -return a RoleResult object
  //
  // -exception IllegalArgumentException  if null parameter
  // -exception RelationServiceNotRegisteredException  if the Relation
  //  Service is not registered in the MBean Server
  // -exception RelationTypeNotFoundException if:
  //  - relation MBean
  //  and
  //  - unknown relation type
  // -exception RelationNotFoundException if:
  //  - relation MBean
  // and
  // - not added in the RS
  RoleResult setRolesInt(RoleList list,
      boolean relationServCallFlg,
      RelationService relationServ)
      throws IllegalArgumentException,
      RelationServiceNotRegisteredException,
      RelationTypeNotFoundException,
      RelationNotFoundException {

    if (list == null ||
        (relationServCallFlg && relationServ == null)) {
      String excMsg = "Invalid parameter.";
      throw new IllegalArgumentException(excMsg);
    }

    RELATION_LOGGER.entering(RelationSupport.class.getName(),
        "setRolesInt",
        new Object[]{list, relationServCallFlg, relationServ});

    RoleList roleList = new RoleList();
    RoleUnresolvedList roleUnresList = new RoleUnresolvedList();

    for (Role currRole : list.asList()) {

      Object currResult = null;
      // Can throw:
      // RelationServiceNotRegisteredException,
      // RelationTypeNotFoundException
      //
      // Will not throw, due to parameters, RoleNotFoundException or
      // InvalidRoleValueException, but catch them to keep compiler
      // happy
      try {
        currResult = setRoleInt(currRole,
            relationServCallFlg,
            relationServ,
            true);
      } catch (RoleNotFoundException exc1) {
        // OK : Do not throw a RoleNotFoundException.
      } catch (InvalidRoleValueException exc2) {
        // OK : Do not throw an InvalidRoleValueException.
      }

      if (currResult instanceof Role) {
        // Can throw IllegalArgumentException if role is null
        // (normally should not happen :(
        try {
          roleList.add((Role) currResult);
        } catch (IllegalArgumentException exc) {
          throw new RuntimeException(exc.getMessage());
        }

      } else if (currResult instanceof RoleUnresolved) {
        // Can throw IllegalArgumentException if role is null
        // (normally should not happen :(
        try {
          roleUnresList.add((RoleUnresolved) currResult);
        } catch (IllegalArgumentException exc) {
          throw new RuntimeException(exc.getMessage());
        }
      }
    }

    RoleResult result = new RoleResult(roleList, roleUnresList);

    RELATION_LOGGER.exiting(RelationSupport.class.getName(), "setRolesInt");
    return result;
  }

  // Initializes all members
  //
  // -param relationId  relation identifier, to identify the relation in the
  // Relation Service.
  // Expected to be unique in the given Relation Service.
  // -param relationServiceName  ObjectName of the Relation Service where
  // the relation will be registered.
  // It is required as this is the Relation Service that is aware of the
  // definition of the relation type of given relation, so that will be able
  // to check update operations (set). Direct access via the Relation
  // Service (RelationService.setRole()) do not need this information but
  // as any user relation is a MBean, setRole() is part of its management
  // interface and can be called directly on the user relation MBean. So the
  // user relation MBean must be aware of the Relation Service where it will
  // be added.
  // -param relationTypeName  Name of relation type.
  // Expected to have been created in given Relation Service.
  // -param list  list of roles (Role objects) to initialized the
  // relation. Can be null.
  // Expected to conform to relation info in associated relation type.
  //
  // -exception InvalidRoleValueException  if the same name is used for two
  //  roles.
  // -exception IllegalArgumentException  if a required value (Relation
  //  Service Object Name, etc.) is not provided as parameter.
  private void initMembers(String relationId,
      ObjectName relationServiceName,
      MBeanServer relationServiceMBeanServer,
      String relationTypeName,
      RoleList list)
      throws InvalidRoleValueException,
      IllegalArgumentException {

    if (relationId == null ||
        relationServiceName == null ||
        relationTypeName == null) {
      String excMsg = "Invalid parameter.";
      throw new IllegalArgumentException(excMsg);
    }

    RELATION_LOGGER.entering(RelationSupport.class.getName(),
        "initMembers", new Object[]{relationId, relationServiceName,
            relationServiceMBeanServer, relationTypeName, list});

    myRelId = relationId;
    myRelServiceName = relationServiceName;
    myRelServiceMBeanServer = relationServiceMBeanServer;
    myRelTypeName = relationTypeName;
    // Can throw InvalidRoleValueException
    initRoleMap(list);

    RELATION_LOGGER.exiting(RelationSupport.class.getName(), "initMembers");
    return;
  }

  // Initialize the internal role map from given RoleList parameter
  //
  // -param list  role list. Can be null.
  //  As it is a RoleList object, it cannot include null (rejected).
  //
  // -exception InvalidRoleValueException  if the same role name is used for
  //  several roles.
  //
  private void initRoleMap(RoleList list)
      throws InvalidRoleValueException {

    if (list == null) {
      return;
    }

    RELATION_LOGGER.entering(RelationSupport.class.getName(),
        "initRoleMap", list);

    synchronized (myRoleName2ValueMap) {

      for (Role currRole : list.asList()) {

        // No need to check if role is null, it is not allowed to store
        // a null role in a RoleList :)
        String currRoleName = currRole.getRoleName();

        if (myRoleName2ValueMap.containsKey(currRoleName)) {
          // Role already provided in current list
          StringBuilder excMsgStrB = new StringBuilder("Role name ");
          excMsgStrB.append(currRoleName);
          excMsgStrB.append(" used for two roles.");
          throw new InvalidRoleValueException(excMsgStrB.toString());
        }

        myRoleName2ValueMap.put(currRoleName,
            (Role) (currRole.clone()));
      }
    }

    RELATION_LOGGER.exiting(RelationSupport.class.getName(), "initRoleMap");
    return;
  }

  // Callback used by the Relation Service when a MBean referenced in a role
  // is unregistered.
  // The Relation Service will call this method to let the relation
  // take action to reflect the impact of such unregistration.
  // Current implementation is to set the role with its current value
  // (list of ObjectNames of referenced MBeans) without the unregistered
  // one.
  //
  // -param objectName  ObjectName of unregistered MBean
  // -param roleName  name of role where the MBean is referenced
  // -param relationServCallFlg  true if call from the Relation Service; this
  //  will happen if the current RelationSupport object has been created by
  //  the Relation Service (via createRelation()) method, so direct access is
  //  possible.
  // -param relationServ  reference to Relation Service object, if internal
  //  relation
  //
  // -exception IllegalArgumentException if null parameter
  // -exception RoleNotFoundException  if:
  //  - the role does not exist
  //  or
  //  - role not writable.
  // -exception InvalidRoleValueException  if value provided for:
  //   - the number of referenced MBeans in given value is less than
  //     expected minimum degree
  //   or
  //   - the number of referenced MBeans in provided value exceeds expected
  //     maximum degree
  //   or
  //   - one referenced MBean in the value is not an Object of the MBean
  //     class expected for that role
  //   or
  //   - a MBean provided for that role does not exist
  // -exception RelationServiceNotRegisteredException  if the Relation
  //  Service is not registered in the MBean Server
  // -exception RelationTypeNotFoundException if unknown relation type
  // -exception RelationNotFoundException if current relation has not been
  //  added in the RS
  void handleMBeanUnregistrationInt(ObjectName objectName,
      String roleName,
      boolean relationServCallFlg,
      RelationService relationServ)
      throws IllegalArgumentException,
      RoleNotFoundException,
      InvalidRoleValueException,
      RelationServiceNotRegisteredException,
      RelationTypeNotFoundException,
      RelationNotFoundException {

    if (objectName == null ||
        roleName == null ||
        (relationServCallFlg && relationServ == null)) {
      String excMsg = "Invalid parameter.";
      throw new IllegalArgumentException(excMsg);
    }

    RELATION_LOGGER.entering(RelationSupport.class.getName(),
        "handleMBeanUnregistrationInt", new Object[]{objectName,
            roleName, relationServCallFlg, relationServ});

    // Retrieves current role value
    Role role;
    synchronized (myRoleName2ValueMap) {
      role = (myRoleName2ValueMap.get(roleName));
    }

    if (role == null) {
      StringBuilder excMsgStrB = new StringBuilder();
      String excMsg = "No role with name ";
      excMsgStrB.append(excMsg);
      excMsgStrB.append(roleName);
      throw new RoleNotFoundException(excMsgStrB.toString());
    }
    List<ObjectName> currRoleValue = role.getRoleValue();

    // Note: no need to test if list not null before cloning, null value
    //       not allowed for role value.
    List<ObjectName> newRoleValue = new ArrayList<ObjectName>(currRoleValue);
    newRoleValue.remove(objectName);
    Role newRole = new Role(roleName, newRoleValue);

    // Can throw InvalidRoleValueException,
    // RelationTypeNotFoundException
    // (RoleNotFoundException already detected)
    Object result =
        setRoleInt(newRole, relationServCallFlg, relationServ, false);

    RELATION_LOGGER.exiting(RelationSupport.class.getName(),
        "handleMBeanUnregistrationInt");
    return;
  }

}
